package com.example.kotlin.widget.chart

import android.content.Context
import android.graphics.*
import android.util.AttributeSet
import android.util.TypedValue
import android.view.MotionEvent
import android.view.View

class BarChartView @JvmOverloads constructor(context: Context?, attrs: AttributeSet? = null) :
    View(context, attrs) {
    private var mBarPaint: Paint? = null
    private var mAxisPaint: Paint? = null
    private lateinit var mDataList //柱状图数据列表
            : FloatArray
    private lateinit var mHorizontalAxis //水平方向x轴坐标
            : Array<String>
    private var mBarWidth = 0f
    private var mMax //数据集合的最大值
            = 0f
    private var mGap //坐标文本与柱状条之间间隔的变量
            = 0
    private var mRadius = 0
    private var mSelectedIndex = -1
    private var enableGrowAnimation = true
    private var mTextRect: Rect? = null
    private var mTemp: RectF? = null
    private val mBars: MutableList<Bar> = ArrayList()
    init {
        init() //初始化方法
    }
    private fun init() {
        mAxisPaint = Paint()
        mAxisPaint!!.isAntiAlias = true
        mAxisPaint!!.textSize = 20f
        mAxisPaint!!.textAlign = Paint.Align.CENTER
        mBarPaint = Paint()
        mBarPaint!!.color = Color.BLUE
        mBarPaint!!.isAntiAlias = true
        mTextRect = Rect()
        mTemp = RectF()
        //柱状条宽度 默认8dp
        mBarWidth =
            TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 8f, resources.displayMetrics)
        //柱状条与坐标文本之间的间隔大小，默认8dp
        mGap = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 8f, resources.displayMetrics)
            .toInt()
    }

    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        mBars.clear() //清空柱状条Bar的集合

        //去除padding，计算绘制所有柱状条所占的宽和高
        val width = w - paddingLeft - paddingRight
        val height = h - paddingTop - paddingBottom

        //按照数据集合的大小平分宽度
        val step = width / mDataList.size

        //mBarWidth为柱状条宽度的变量，可以设置，mRadius为柱状条宽度的一半
        mRadius = (mBarWidth / 2).toInt()

        //计算第一条柱状条的左边位置
        var barLeft = paddingLeft + step / 2 - mRadius

        //通过坐标文本画笔计算绘制x轴第一个坐标文本占据的矩形边界，这里主要获取其高度，为计算maxBarHeight提供数据
        mAxisPaint!!.getTextBounds(mHorizontalAxis[0], 0, mHorizontalAxis[0].length, mTextRect)

        //计算柱状条高度的最大像素大小，mTextRect.height为底部x轴坐标文本的高度，mGap为坐标文本与柱状条之间间隔大小的变量
        val maxBarHeight = height - mTextRect!!.height() - mGap

        //计算柱状条最大像素大小与最大数据值的比值
        val heightRatio = maxBarHeight / mMax

        //遍历数据集合，初始化所有的柱状条Bar对象
        for (data in mDataList) {
            val bar: Bar = Bar() //创建柱状条对象
            bar.value = data //设置原始数据
            bar.transformedValue = bar.value * heightRatio //计算原始数据对应的像素高度大小
            //计算绘制柱状条的四个位置
            bar.left = barLeft
            bar.top = (paddingTop + maxBarHeight - bar.transformedValue).toInt()
            bar.right = (barLeft + mBarWidth).toInt()
            bar.bottom = paddingTop + maxBarHeight

            //初始化绘制柱状条时当前的top值，用作动画
            bar.currentTop = bar.bottom

            //将初始化好的Bar添加到集合中
            mBars.add(bar)
            //更新柱状条左边位置，为初始化下一个Bar对象做准备
            barLeft += step
        }
    }

    /**
     * 绘制柱状图
     */
    override fun onDraw(canvas: Canvas) {
        if (enableGrowAnimation) {
            drawBarsWidthAnimation(canvas)
        } else {
            drawBars(canvas)
        }
    }

    /**
     * 绘制柱状条，带自增长动画
     */
    private fun drawBarsWidthAnimation(canvas: Canvas) {
        //遍历所有的Bar
        for (i in mDataList.indices) {
            val bar = mBars[i]

            //绘制坐标文本
            val axis = mHorizontalAxis[i]
            val textX = (bar.left + mRadius).toFloat()
            val textY = (height - paddingBottom).toFloat()
            canvas.drawText(axis, textX, textY, mAxisPaint!!)

            //更新当前柱状条顶部位置变量，BAR_GROW_STEP为柱状条增长的步长，即让柱状条长高BAR_GROW_STEP长度
            bar.currentTop -= BAR_GROW_STEP

            //当计算出来的currentTop小于柱状条本来的top值时，说明越界了
            if (bar.currentTop <= bar.top) {
                //将currentTop重置成本来的top值，解决越界问题
                bar.currentTop = bar.top

                //高度最高的柱状条的顶部位置为paddingTop，如果currentTop等于paddingTop，说明高度最高的进度条也到达
                //其最高点，可以停止增长动画了，于是将enableGrowAnimation置为false
                if (bar.currentTop == paddingTop) {
                    enableGrowAnimation = false
                }
            }

            //绘制圆角柱状条
            mTemp!![bar.left.toFloat(), bar.currentTop.toFloat(), bar.right.toFloat()] =
                bar.bottom.toFloat()
            canvas.drawRoundRect(mTemp!!, mRadius.toFloat(), mRadius.toFloat(), mBarPaint!!)
        }

        //延时触发重新绘制，调用onDraw方法
        if (enableGrowAnimation) {
            postInvalidateDelayed(DELAY.toLong())
        }
    }

    /**
     * 绘制柱状条，没有动画效果的模式
     */
    private fun drawBars(canvas: Canvas) {
        //遍历所有的Bar对象，一个个绘制
        for (i in mBars.indices) {
            val bar = mBars[i]
            //绘制底部x轴坐标文本
            val axis = mHorizontalAxis[i] //获取对应位置的坐标文本
            //计算绘制文本的起始位置坐标(textX，textY)，textX为柱状条的中线位置，由于我们对画笔mAxisPaint设置了
            //Paint.Align.CENTER，所以绘制出来的文本的中线与柱状条的中线是重合的
            val textX = (bar.left + mRadius).toFloat()
            val textY = (height - paddingBottom).toFloat()
            //绘制坐标文本
            canvas.drawText(axis, textX, textY, mAxisPaint!!)
            if (i == mSelectedIndex) {
                mBarPaint!!.color = Color.RED
                val x = (bar.left + mRadius).toFloat()
                val y = (bar.top - mGap).toFloat()
                canvas.drawText(bar.value.toString(), x, y, mAxisPaint!!)
            } else {
                mBarPaint!!.color = Color.BLUE
            }
            //设置柱状条矩形
            mTemp!![bar.left.toFloat(), bar.top.toFloat(), bar.right.toFloat()] =
                bar.bottom.toFloat()
            //绘制圆角矩形
            canvas.drawRoundRect(mTemp!!, mRadius.toFloat(), mRadius.toFloat(), mBarPaint!!)
            //绘制直角矩形
            //canvas.drawRect(mTemp, mBarPaint);
        }
    }

    /**
     * 设置水平方向x轴坐标值
     * @param horizontalAxis 坐标值数组，如{"1", "2", "3", "4","5", "6", "7", "8", "9", "10", "11", "12"}
     */
    fun setHorizontalAxis(horizontalAxis: Array<String>) {
        mHorizontalAxis = horizontalAxis
    }

    /**
     * 设置柱状图数据
     * @param dataList 数据数组，如{12, 24, 45, 56, 89, 70, 49, 22, 23, 10, 12, 3}
     * @param max 数据数组中的最大值，如89，最大值用来计算绘制时的高度比例
     */
    fun setDataList(dataList: FloatArray, max: Int) {
        mDataList = dataList
        mMax = max.toFloat()
    }

    private inner class Bar {
        //绘制柱状条的四个位置
        var left = 0
        var top = 0
        var right = 0
        var bottom = 0
        var value //柱状条原始数据的大小
                = 0f
        var transformedValue //柱状条原始数据大小转换成对应的像素大小
                = 0f
        var currentTop //柱状图动画中用到，表示柱状条动画过程中顶部位置的变量，取值范围为[0,top]
                = 0

        fun isInside(x: Float, y: Float): Boolean {
            return x > left && x < right && y > top && y < bottom
        }
    }

    override fun onTouchEvent(event: MotionEvent): Boolean {
        if (enableGrowAnimation) {
            return false
        }
        when (event.actionMasked) {
            MotionEvent.ACTION_DOWN -> {
                var i = 0
                while (i < mBars.size) {
                    if (mBars[i].isInside(event.x, event.y)) {
                        enableGrowAnimation = false
                        mSelectedIndex = i
                        invalidate()
                        break
                    }
                    i++
                }
            }
            MotionEvent.ACTION_UP -> {
                mSelectedIndex = -1
                enableGrowAnimation = false
                invalidate()
            }
        }
        return true
    }

    fun setSelectedIndex(selectedIndex: Int) {
        mSelectedIndex = selectedIndex
    }

    fun setEnableGrowAnimation(enableGrowAnimation: Boolean) {
        this.enableGrowAnimation = enableGrowAnimation
    }

    companion object {
        private const val TAG = "BarChartView"
        private const val DELAY = 10
        private const val BAR_GROW_STEP = 15 //柱状条增长动画步长
    }


}